google.load('visualization', '1', {packages:['table']});
google.setOnLoadCallback(initTable);
var directionsService = new google.maps.DirectionsService();
var map,data,geocoder,autocomplete1,autocomplete2,searchBounds,startPlace1,startPlace2,link,lastInfo,startParam,touchParam,boundsParam,spfLayer,medLayer;
var startField1,startField2,directionsDisplay1,directionsDisplay2,linkArea,gplusPanel,mode1,mode2,routesTable,durFormatter,distFormatter;
var markersArray = [];
var touchPointArray = [];
var touchDetails = [];
var routeArray = [];
var startPoints = [];
var touchPoints = [];
var fusionRows = [];
var fusionSpfRows = [];
var MAX_TOUCHPOINTS = 15;
var HOME_DEFAULT = new google.maps.LatLng(1.358798,103.963591);
var ZOOM_DEFAULT = 10;
var searchType = "MEET";//'MED' 'MEET' 'GOVT'
var medTypes = ["doctor","dentist","health","hospital","hospital","physiotherapist"];
var meetTypes = ["cafe","bar","restaurant","park","bakery","bowling_alley","food","library","shopping_mall"];


function Route(rank, touch, dur_max, dist_max, dist_total) {//Custom Class
	this.rank = rank;
	this.touch = touch;
	this.dur_max = dur_max;
	this.dist_max = dist_max;
	this.dist_total = dist_total;
}

function initialize() {
	var placeTypeParam = getURLParameter('placeType');
	if(placeTypeParam && placeTypeParam != null && placeTypeParam != 'null') {
		if(placeTypeParam == 'MEET' || placeTypeParam == 'meet') {
			searchType = 'MEET';
		} else if(placeTypeParam == 'MED' || placeTypeParam == 'med') {
			searchType = 'MED';
		} else if(placeTypeParam == 'GOVT' || placeTypeParam == 'govt') {
			searchType = 'GOVT';
		}
		console.log('setPlaceType placeTypeParam: ' + placeTypeParam + ', searchType: ' + searchType );
	}
	showInputs();
	geocoder = new google.maps.Geocoder();
	linkArea = document.getElementById('linkArea');
	var mapOptions = {
		center: HOME_DEFAULT,
		zoom: ZOOM_DEFAULT,
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	map = new google.maps.Map(document.getElementById('map'), mapOptions);
	directionsDisplay1 = new google.maps.DirectionsRenderer({preserveViewport: true, panel:directionPanel1});
	directionsDisplay2 = new google.maps.DirectionsRenderer({preserveViewport: true, panel:directionPanel2});

	startField1 = document.getElementById('startField1');
	autocomplete1 = new google.maps.places.Autocomplete(startField1);
	autocomplete1.bindTo('bounds', map);
	mode1 = document.getElementById('mode1');

	startField2 = document.getElementById('startField2');
	autocomplete2 = new google.maps.places.Autocomplete(startField2);
	autocomplete2.bindTo('bounds', map);
	mode2 = document.getElementById('mode2');

	startParam = getURLParameter('start');
	touchParam = getURLParameter('touch');
	boundsParam = getURLParameter('bounds');
	if(startParam && startParam != null && startParam != 'null') {
		processParams();
	} else {
		if(navigator.geolocation) {
			navigator.geolocation.getCurrentPosition(function(position) {
				console.log('geolocation detected: ' + position + ' at timestamp: ' + position.timestamp);
				var pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
				map.setCenter(pos);//TODO do before map init
				geocoder.geocode({'latLng': pos}, function(results, status) {
					if (status == google.maps.GeocoderStatus.OK) {
						if (results[0]) {
							startPlace1 = results[0];
							startField1.value = results[0].formatted_address;
						}
					} else {
						console.log("Geocoder failed with status: " + status);
					}
				});
			}, function() {
				console.log('HTML5 Geolocation failed');
			});
		} else {
			console.log('Your browser doesn\'t support location detection, please enter your location');
			alert('Your browser doesn\'t support HTML5 Geolocation, please enter your position manually');
		}
	}
	try {
		gplusPanel = document.getElementById('gplusPanel');
	} catch(e) {
		console.log('Error in finding GPlus panel. ' + e.message);
	}
}

function initTable() {
	routesTable = new google.visualization.Table(document.getElementById('routesTable'));
	durFormatter = new google.visualization.NumberFormat({fractionDigits: 1});
	distFormatter = new google.visualization.NumberFormat({fractionDigits: 2});
}

function storeFusionData(response) {
	if (response.rows) {
		fusionRows = response.rows;
	} else {
		console.log("Fusion table error: " + response);
	}
}

function storeSpfFusionData(response) {
	if (response.rows) {
		fusionSpfRows = response.rows;
	} else {
		console.log("Fusion SPF table error: " + response);
	}
}

function processParams() {
	if(boundsParam && boundsParam != null && boundsParam != 'null') {
		setBoundsByParam();
	}
	if(startParam && startParam != null && startParam != 'null') {
		var st = startParam.split('+');
		console.log('st: ' + st);
		if(st && st.length > 1) {
			var address1 = getParamAddress(st[0]);
			var geometry1 = {'location':getParamLatLng(st[0])};
			startPlace1 = {'name': address1,'formatted_address': address1,'geometry': geometry1};
			startField1.value = startPlace1.formatted_address;
			var address2 = getParamAddress(st[1]);
			var geometry2 = {'location':getParamLatLng(st[1])};
			startPlace2 = {'name': address2,'formatted_address': address2,'geometry': geometry2};
			startField2.value = startPlace2.formatted_address;
		}
		if(touchParam && touchParam != null && touchParam != 'null') {
			var ta = touchParam.split('+');
			console.log('ta: ' + ta);
			for ( var i = 0; i < ta.length; i++) {
				var a = getParamAddress(ta[i]);
				var g = {'location':getParamLatLng(ta[i])};
				r = {'name': a,'formatted_address': a,'geometry': g};//TODO get from places service
				touchDetails.push(r);
				createMarker(r);
				touchPointArray.push(r.geometry.location);
				processParamGeoResults();
			}
		}
	}
}

function setBoundsByParam() {
	var bi = boundsParam.indexOf('),(');
	var sws = boundsParam.substr(2,bi-2);
	var nes = boundsParam.substr(bi+3,boundsParam.indexOf('))')-bi-3);
	var sw = sws.split(',');
	var ne = nes.split(',');
	console.log('setBoundsByParam boundsParam: ' + boundsParam);
	searchBounds = new google.maps.LatLngBounds(new google.maps.LatLng(Number(sw[0]), Number(sw[1])), new google.maps.LatLng(Number(ne[0]), Number(ne[1])));
}

function getParamLatLng(p) {
	var i = p.indexOf('(');
	var l = p.substr(i+1,p.indexOf(')')-i-1);
	var la = l.split(',');
	return new google.maps.LatLng(Number(la[0]),Number(la[1]));
}

function getParamAddress(p) {
	return p.substr(0,p.indexOf('('));
}

function processParamGeoResults() {
	if(touchParam && touchParam != null && touchParam != 'null') {
		if(startPlace1 && startPlace1 != null && startPlace1 != 'null'
				&& (searchType != 'MEET' || (startPlace2 && startPlace2 != null && startPlace2 != 'null'))
				&& touchDetails && touchDetails != null && touchDetails != 'null' && touchDetails.length != 0) {
			console.log('processParamGeoResults with touch response');
			getDistanceMatrix();
		} else {
			console.log('touchDetails: ' + touchDetails);
			console.log('touchDetails.length: ' + touchDetails.length);
		}
	} else if(startPlace1 && startPlace1 != null && startPlace1 != 'null'
			&& (searchType != 'MEET' || (startPlace2 && startPlace2 != null && startPlace2 != 'null'))) {
		console.log('processParamGeoResults without touch');
		searchTouchPoints();
	}
}

function processUserReq() {
	if(autocomplete1.getPlace())
		startPlace1 = autocomplete1.getPlace();
	if(searchType == 'MEET' && autocomplete2.getPlace())
		startPlace2 = autocomplete2.getPlace();
	if(!startPlace1 || startPlace1 == null || startPlace1 == 'null' || startPlace1 == ''
		|| (searchType == 'MEET' && (!startPlace2 || startPlace2 == null || startPlace2 == 'null' || startPlace2 == ''))) {
		alert('Please select the Start Points first');
	} else {
		if(searchType == 'MEET') {
			setExtendedBounds(startPlace1.geometry.location, startPlace2.geometry.location);
		} else {
			setExtendedBounds(startPlace1.geometry.location, startPlace1.geometry.location);
		}
		searchTouchPoints();
	}
}

function searchTouchPoints() {
	if(searchType == 'GOVT') {
		spfLayer = new google.maps.FusionTablesLayer({
			query: {
				select: 'geometry',
				from: '17CDTnF9Ubq2UeaOmQ_nQWQ6UiXEnLOSBN0Mop5Q'
			},
			map: map
		});
		google.maps.event.addListener(spfLayer, 'load', function(e) {
			console.log("spfLayer loaded");
		});
		for (var i = 0; i < fusionSpfRows.length; i++) {
			var fusionSpfResult = getFusionSpfResultFromRow(fusionSpfRows[i], startPlace1.geometry.location);
			if(touchDetails.length < MAX_TOUCHPOINTS) {
				touchDetails.push(fusionSpfResult);
				touchDetails.sort(sortFusionSpfResults);
				
			} else {
				if(touchDetails[MAX_TOUCHPOINTS - 1].dist > fusionSpfResult.dist) {
					touchDetails[MAX_TOUCHPOINTS - 1] = fusionSpfResult;
					touchDetails.sort(sortFusionSpfResults);
				}
			}
		}
		for (var i = 0; i < touchDetails.length; i++) {
			touchPointArray.push(touchDetails[i].geometry.location);
		}
		getDistanceMatrix();
	} else {
		if(searchType == 'MEET') {
			var searchRequest = {bounds: searchBounds, types: meetTypes};
		} else if(searchType == 'MED') {
			var searchRequest = {bounds: searchBounds, types: medTypes};
		}
		var placesService = new google.maps.places.PlacesService(map);
		placesService.search(searchRequest, touchPointsCallback);
		if(searchType == 'MED') {
			medLayer = new google.maps.FusionTablesLayer({
				query: {
					select: 'Latitude',
					from: '1alijL-Ib3_Mjiash2A8tcJosG0eljkT0mNBXNd8',
					where: 'ST_INTERSECTS(' + 'Latitude' +
		                  ', RECTANGLE(LATLNG' + searchBounds.getSouthWest() + ', LATLNG' + searchBounds.getNorthEast() + '))'
				},
				map: map
			});
			google.maps.event.addListener(medLayer, 'load', function(e) {
				console.log("medLayer loaded");
			});
		}
	}
}

function touchPointsCallback(searchResults, status) {
	if (status == google.maps.places.PlacesServiceStatus.OK) {
		clearData();
		if(searchType == 'MED') {
			for (var i = 0; i < fusionRows.length && i < MAX_TOUCHPOINTS; i++) {
				var fusionResult = getFusionResultFromRow(fusionRows[i]);
				touchDetails.push(fusionResult);
				touchPointArray.push(fusionResult.geometry.location);
			}
		}
		for (var i = 0; i < searchResults.length && i < MAX_TOUCHPOINTS; i++) {
			touchDetails.push(searchResults[i]);
			createMarker(searchResults[i]);
			touchPointArray.push(searchResults[i].geometry.location);
		}
		getDistanceMatrix();
	} else {
		console.log('touchPointsCallback failed. PlacesServiceStatus status: ' + status);
	}
}
function getFusionResultFromRow(cols) {
	var loc = new google.maps.LatLng(Number(cols[1]),Number(cols[2]));
	return fusionResult = {'name': cols[3],'formatted_address': cols[4],'geometry': {'location':loc}};
}
function getFusionSpfResultFromRow(cols, start1Loc) {
	var loc = new google.maps.LatLng(Number(cols[2].geometry.coordinates[1]), Number(cols[2].geometry.coordinates[0]));
	var dist = distance(Number(cols[2].geometry.coordinates[1]), Number(cols[2].geometry.coordinates[0]), start1Loc.lat(), start1Loc.lng());
	return fusionResult = {'name': cols[1],'formatted_address': cols[0],'geometry': {'location':loc},'dist': dist};
}

var sortFusionSpfResults = function (a, b){
	return (Number(a.dist) - Number(b.dist)) //Makes FusionSpfResults to be sorted by distance from start point and ascending
}

function getDistanceMatrix() {
	console.log('zooming to searchBounds: ' + searchBounds);
	map.fitBounds(searchBounds);
	var matrixService = new google.maps.DistanceMatrixService();
	if(searchType == 'MEET') {
		var matrixOrigins = [startPlace1.geometry.location, startPlace2.geometry.location];
	} else {
		var matrixOrigins = [startPlace1.geometry.location];
	}
	matrixService.getDistanceMatrix({
		origins: matrixOrigins,
		destinations: touchPointArray,
		travelMode: google.maps.TravelMode.DRIVING,//TODO handle - Transit mode not supported
		unitSystem: google.maps.UnitSystem.METRIC,
		avoidHighways: false,
		avoidTolls: false
	}, matrixCallback);
}

function matrixCallback(response, status) {
	if (status == google.maps.DistanceMatrixStatus.OK) {
		startPoints = response.originAddresses;
		touchPoints = response.destinationAddresses;
		for (var t = 0; t < touchPoints.length; t++) {
			var dist_total = 0;
			var dur_total = 0;
			var dist_max = 0;
			var dur_max = 0;
			for(var e = 0; e < startPoints.length; e++) {
				var results = response.rows[e].elements;
				dur_total += results[t].duration.value;
				dist_total += results[t].distance.value;
				if(results[t].duration.value > dur_max) {
					dur_max = results[t].duration.value;
				}
				if(results[t].distance.value > dist_max) {
					dist_max = results[t].distance.value;
				}
			}
			var address = (touchDetails[t].formatted_address?touchDetails[t].formatted_address:touchPoints[t]);
			var r = new Route(t, address, dur_max, dist_max, dist_total)
			routeArray.push(r);
		}
		console.log('routeArray.length: ' + routeArray.length);
		routeArray.sort(function(a,b){return a.dur_max - b.dur_max});
		buildTable(routeArray, response);
	} else {
		console.log('matrixCallback failed. DistanceMatrixStatus status: ' + status);
	}
}

function buildTable(matrixResponse) {
	data = new google.visualization.DataTable();
	data.addColumn('number', 'Rank');
	data.addColumn('string', 'Name');
	data.addColumn('string', 'Location');
	data.addColumn('number', 'Duration (minutes)');
	//data.addColumn('number', 'Duration');
	data.addColumn('number', 'Max Distance (km)');
	data.addColumn('number', 'Total Distance (km)');
	for (var r = 0; r < routeArray.length; r++) {
		var route = routeArray[r];
		data.addRow([(route.rank + 1),
			touchDetails[route.rank].name,
			route.touch,
			(route.dur_max/60),
			//{v:toDecimal1(route.dur_max/60), f:toDecimal1(route.dur_max/60) + ' as max(' + matrixResponse.rows[0].elements[route.rank].duration.value + ',' + matrixResponse.rows[1].elements[route.rank].duration.value + ')'},
			(route.dist_max/1000),
			(route.dist_total/1000)
		]);
	}
	durFormatter.format(data, 3);
	distFormatter.format(data, 4);
	distFormatter.format(data, 5);
	routesTable.draw(data, {showRowNumber: true});
	google.visualization.events.addListener(routesTable, 'select', showSelectedRoute);
	showRoute(0);
}

function showSelectedRoute() {
	var selectedRows = routesTable.getSelection();
	for (var s = 0; s < selectedRows.length; s++) {
		var selectedRank = Number(data.getValue(selectedRows[s].row, 0)) - 1;
		showRouteByRank(selectedRank);
	}
}

function showRouteByRank(rank) {
	console.log('showRouteByRank: ' + rank);
	var routeIndex = 0;
	for (var i = 0; i < routeArray.length; i++) {
		if(rank == routeArray[i].rank) {
			routeIndex = i;
		}
	}
	showRoute(routeIndex);
}

function showRoute(routeIndex) {
	console.log('showRoute routeIndex: ' + routeIndex);
	var route = routeArray[routeIndex];
	start1 = startPoints[0];
	var touchPoint = route.touch;
	console.log('showRoute touchPoint: ' + touchPoint);
	var request1 = {
		origin: start1,
		destination: touchPoint,
		travelMode: google.maps.DirectionsTravelMode[mode1.value]
	};
	directionsService.route(request1, function(response, status) {
		if (status == google.maps.DirectionsStatus.OK) {
			directionsDisplay1.setMap(map);
			directionsDisplay1.setDirections(response);
		} else {
			console.log("Route call on DirectionsService1 failed with status: " + status + ". Response: " + response.toString());
		}
	});
	if(searchType == 'MEET') {
		start2 = startPoints[1];
		var request2 = {
			origin: start2,
			destination: touchPoint,
			travelMode: google.maps.DirectionsTravelMode[mode2.value]
		};
		directionsService.route(request2, function(response, status) {
			if (status == google.maps.DirectionsStatus.OK) {
				directionsDisplay2.setMap(map);
				directionsDisplay2.setDirections(response);
			} else {
				console.log("Route call on DirectionsService2 failed with status: " + status + ". Response: " + response.toString());
				directionsDisplay2.setMap(null);
			}
		});
	}
	showResults();
	setLink(route.rank);
}

function setLink(t) {
	console.log('Setting link for rank: ' + t);
	console.log('setLink touchDetails[' + t + '].name: ' + touchDetails[t].name);
	link = encodeURI('http://www.nearbymap.com/?start='
			+ startPlace1.formatted_address + startPlace1.geometry.location.toString().replace(/\s/g,"")
			+ ((searchType == 'MEET')?('+' + startPlace2.formatted_address + startPlace2.geometry.location.toString().replace(/\s/g,"")):'')
			+ '&touch=' + touchDetails[t].name + ' ' + touchDetails[t].vicinity + touchDetails[t].geometry.location.toString().replace(/\s/g,"")
			+ '&bounds=' + searchBounds.toString().replace(/\s/g,""));
	linkArea.value = link;
	try {
		gplusPanel.setAttribute('data-href', link);
	} catch(e) {
		console.log('Error in setting GPlus link. ' + e.message);
	}
}

function showInputs(placeTypeArg) {
	if(placeTypeArg && placeTypeArg != null) {
		searchType = placeTypeArg;
		console.log('searchType: ' + searchType);
	}
	document.getElementById('map').style.display = 'block';
	document.getElementById('headers').style.display = 'inline';
	document.getElementById('inputs').style.display = 'inline';
	if(searchType == 'MED') {
		document.getElementById('meetHeader').style.display = 'none';
		document.getElementById('medHeader').style.display = 'inline';
		document.getElementById('govtHeader').style.display = 'none';
		document.getElementById('inputSpeciality').style.display = 'inline';
		document.getElementById('inputMode2').style.display = 'none';
		document.getElementById('aMed').className = 'here';
		document.getElementById('aMeet').className = 'there';
		document.getElementById('aGovt').className = 'there';
		
	} else if(searchType == 'MEET') {
		document.getElementById('meetHeader').style.display = 'inline';
		document.getElementById('medHeader').style.display = 'none';
		document.getElementById('govtHeader').style.display = 'none';
		document.getElementById('inputSpeciality').style.display = 'none';
		document.getElementById('inputMode2').style.display = 'inline';
		document.getElementById('aMed').className = 'there';
		document.getElementById('aMeet').className = 'here';
		document.getElementById('aGovt').className = 'there';
	} else if(searchType == 'GOVT') {
		document.getElementById('meetHeader').style.display = 'none';
		document.getElementById('medHeader').style.display = 'none';
		document.getElementById('govtHeader').style.display = 'inline';
		document.getElementById('inputSpeciality').style.display = 'none';
		document.getElementById('inputMode2').style.display = 'none';
		document.getElementById('aMed').className = 'there';
		document.getElementById('aMeet').className = 'there';
		document.getElementById('aGovt').className = 'here';
	}
	clearData();
}

function showResults() {
	document.getElementById('resultPanel').style.display = 'inline';
}

function setExtendedBounds(point1, point2) {
	var DELTA = 0.04;
	south = Math.min(point1.lat(),point2.lat()) - DELTA;
	north = Math.max(point1.lat(),point2.lat()) + DELTA;
	west = Math.min(point1.lng(),point2.lng()) - DELTA;
	east = Math.max(point1.lng(),point2.lng()) + DELTA;
	searchBounds = new google.maps.LatLngBounds(new google.maps.LatLng(south, west), new google.maps.LatLng(north, east));
}

function createMarker(place) {
	var image;
	if (typeof place.icon === "undefined") {
		image = null;
	} else {
		var image = new google.maps.MarkerImage(
			place.icon,
			new google.maps.Size(71, 71),
			new google.maps.Point(0,0),
			new google.maps.Point(17, 34),
			new google.maps.Size(15,15));
	}
	var marker = new google.maps.Marker({
		title: place.name,
		map: map,
		icon: image,
		position: place.geometry.location
	});
	markersArray.push(marker);
	listenMarker(marker, place);
}

function listenMarker(marker, place) {
	var info = new google.maps.InfoWindow({content: place.name + '<br/>Type: ' + place.types + '<br/>Address: ' + place.vicinity + '<br/>Rating: ' + place.rating});
	google.maps.event.addListener(marker, 'click', function() {
		if(lastInfo)
			lastInfo.close();
		info.open(map, marker);
		lastInfo = info;
	});
}

function clearData() {
	if (markersArray) {
		for (i in markersArray) {
			markersArray[i].setMap(null);
		}
		markersArray.length = 0;
	}
	touchPointArray = [];
	touchDetails = [];
	routeArray = [];
	startPoints = [];
	touchPoints = [];
	if(spfLayer)
		spfLayer.setMap(null);
	if(medLayer)
		medLayer.setMap(null);
	if(directionsDisplay1)
		directionsDisplay1.setMap(null);
	if(directionsDisplay2)
		directionsDisplay2.setMap(null);
	clearDiv('directionPanel1');
	clearDiv('directionPanel2');
	if(routesTable)
		routesTable.clearChart();
	if(linkArea)
		linkArea.value = "";
}
function clearDiv(divId) {
	var node = document.getElementById(divId);
	if(node)
		while (node.hasChildNodes()) {
		    node.removeChild(node.firstChild);
		}
}
function getURLParameter(name) {
    return decodeURI(
        (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]
    );
}

function distance(lat1, lon1, lat2, lon2) {
	var radlat1 = Math.PI * lat1/180;
	var radlat2 = Math.PI * lat2/180;
	var radlon1 = Math.PI * lon1/180;
	var radlon2 = Math.PI * lon2/180;
	var theta = lon1-lon2;
	var radtheta = Math.PI * theta/180;
	var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
	dist = Math.acos(dist);
	dist = dist * 180/Math.PI;
	dist = dist * 60 * 1.1515;
	dist = dist * 1.609344;//in KMs
	return dist
} 
function toInteger(n){return Math.round(Number(n));};
function toDecimal1(n){return Math.round(Number(n)*10)/10;};
function toDecimal2(n){return Math.round(Number(n)*100)/100;};